package com.superbytecode.common.utils;

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;

import java.net.InetSocketAddress;
import java.util.Objects;

/**
 * All rights Reserved, Designed By www.super-bytecode.com
 *
 * @ProjectName: superbytecode-beacon
 * @Package: com.superbytecode.common.utils
 * @ClassName: IPAddressUtil
 * @Description: [IP地址工具类]
 * <p>
 *
 * </p>
 * @Author: [Lance Ting]
 * @Date: 2023/11/15 15:58
 * @Version: V1.0
 * @Copyright: 2023 www.super-bytecode.com Inc. All rights reserved.
 * TODO: 注意,本文件Lance Ting所作,如果转载或使用请标明具体出处!
 **/
@Slf4j
@UtilityClass
public class IPAddressUtil {
    /**
     *
     */
    private static final String X_FORWARDED_FOR = "x-forwarded-for";
    /**
     *
     */
    private static final String PROXY_CLIENT_IP = "Proxy-Client-IP";
    /**
     *
     */
    private static final String WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP";
    /**
     *
     */
    private static final String HTTP_CLIENT_IP = "HTTP_CLIENT_IP";
    /**
     *
     */
    private static final String HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR";
    /**
     *
     */
    private static final String X_REAL_IP = "X-Real-IP";
    /**
     *
     */
    private static final String UNKNOWN = "unknown";

    // -----------------------------------------------------------------------------------------------------------------

    /**
     * 获取客户端的IP地址
     * <p>
     * <p>
     * 获取客户端的IP地址的方法是：request.getRemoteAddr<br/>
     * 但是在通过了Apache，Squid，nginx等反向代理软件就获取的是代理地址，而非客户端真是ip地址
     * </p>
     *
     * @param request 请求上下文
     * @return 请求方真实IP地址
     */
    public String getClientIPAddress(ServerHttpRequest request) {
        // 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst(X_FORWARDED_FOR);
        // 1、过滤通过反向代理的地址
        // 多次反向代理后会有多个IP值，第一个IP才是真实IP,它们按照英文逗号','分割
        if (!invalidIPChecked(ip) && Objects.nonNull(ip) && ip.contains(",")) {
            log.debug("多次反向代理后会有多个ip值，第一个ip才是真实ip: {}", ip);
            ip = ip.split(",")[0];
        }
        //
        if (invalidIPChecked(ip)) {
            ip = headers.getFirst(PROXY_CLIENT_IP);
        }
        //
        if (invalidIPChecked(ip)) {
            ip = headers.getFirst(WL_PROXY_CLIENT_IP);
        }
        //
        if (invalidIPChecked(ip)) {
            ip = headers.getFirst(HTTP_CLIENT_IP);
        }
        //
        if (invalidIPChecked(ip)) {
            ip = headers.getFirst(HTTP_X_FORWARDED_FOR);
        }
        //
        if (invalidIPChecked(ip)) {
            ip = headers.getFirst(X_REAL_IP);
        }
        //
        if (invalidIPChecked(ip)) {
            ip = getIPAddress(request);
        }
        // log.info("获取客户端请求IP地址结果：IP-{},headerName-{}", ip, headerName);
        return ip;
    }

    /**
     * 获取客户端ip地址
     *
     * @param request 请求
     * @return ip地址
     */
    public String getIPAddress(ServerHttpRequest request) {
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        return Objects.isNull(remoteAddress) ? "" : remoteAddress.getHostString();
    }

    // -----------------------------------------------------------------------------------------------------------------

    /**
     * 获取网卡IP地址信息
     *
     * @param request 客户端请求
     * @return ip地址
     */
    /*private String getIPAddressByNetworkCard(ServerHttpRequest request) {
        String ip = request.getRemoteAddr();
        // 127.0.0.1 ipv4, 0:0:0:0:0:0:0:1 ipv6
        if (!Objects.equals(ip, "127.0.0.1") && !Objects.equals(ip, "0:0:0:0:0:0:0:1")) {
            return ip;
        }
        //根据网卡取本机配置的IP
        try {
            InetAddress inet = InetAddress.getLocalHost();
            ip = inet.getHostAddress();
        } catch (UnknownHostException e) {
            log.error("获取网卡IP地址信息异常，", e);
        }
        return ip;
    }*/

    /**
     * 判断ip地址是否无效
     *
     * @param ip IP地址
     * @return true-无效，false-有效
     */
    private boolean invalidIPChecked(String ip) {
        return StringUtils.isEmpty(ip) || Objects.equals(UNKNOWN, ip);
    }
}